package main

import (
	"bufio"
	"embed"
	"fmt"
	"io"
	"log"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"unicode"
)

// HACK: rename of iconMap -> iconMapping
type iconMapping map[string]string

var icons iconMapping

func parseIcons() {
	icons = make(iconMapping)
	icons.parse()
}

//go:embed config-file/icons
var config_file embed.FS

func (im iconMapping) parse() {
	// file: config-file/icons
	icons, _ := config_file.Open("config-file/icons")
	pairs, err := readPairs(icons)

	if err != nil {
		log.Printf("reading icons file: %s", err)
		return
	}

	for _, pair := range pairs {
		key, val := pair[0], pair[1]
		key = replaceTilde(key)

		if filepath.IsAbs(key) {
			key = filepath.Clean(key)
		}
		im[key] = val
	}
}

func (im iconMapping) getIcon(fileinfo os.FileInfo) string {
	if fileinfo.IsDir() {
		if val, ok := im[fileinfo.Name()+"/"]; ok {
			return val
		}
	}

	var key string
	f_is_dir := fileinfo.IsDir()
	f_mode := fileinfo.Mode()

	switch {
	case f_is_dir && f_mode&os.ModeSticky != 0 && f_mode&0002 != 0:
		key = "tw"
	case f_is_dir && f_mode&0002 != 0:
		key = "ow"
	case f_is_dir && f_mode&os.ModeSticky != 0:
		key = "st"
	case f_is_dir:
		key = "di"
	case f_mode&os.ModeSocket != 0:
		key = "so"
	case f_mode&os.ModeDevice != 0:
		key = "bd"
	case f_mode&os.ModeCharDevice != 0:
		key = "cd"
	case f_mode&os.ModeSetuid != 0:
		key = "su"
	case f_mode&os.ModeSetgid != 0:
		key = "sg"
	}

	if val, ok := im[key]; ok {
		return val
	}
	if val, ok := im[fileinfo.Name()+"*"]; ok {
		return val
	}
	if val, ok := im["*"+fileinfo.Name()]; ok {
		return val
	}
	if val, ok := im[filepath.Base(fileinfo.Name())+".*"]; ok {
		return val
	}

	ext := filepath.Ext(fileinfo.Name())
	if val, ok := im["*"+strings.ToLower(ext)]; ok {
		return val
	}

	if f_mode&0111 != 0 {
		if val, ok := im["ex"]; ok {
			return val
		}
	}

	if val, ok := im["fi"]; ok {
		return val
	}

	return " "
}

func replaceTilde(s string) string {
	u, err := user.Current()
	if err != nil {
		log.Printf("user: %s", err)
	}

	if strings.HasPrefix(s, "~") {
		s = strings.Replace(s, "~", u.HomeDir, 1)
	}

	return s
}

// todos: add document
func readPairs(reader io.Reader) ([][]string, error) {
	var pairs [][]string
	s := bufio.NewScanner(reader)

	for s.Scan() {
		line := s.Text()

		squote, dquote := false, false
		for i := 0; i < len(line); i++ {
			if line[i] == '\'' && !dquote {
				squote = !squote
			} else if line[i] == '"' && !squote {
				dquote = !dquote
			}

			if !squote && !dquote && line[i] == '#' {
				line = line[:i]
				break
			}
		}

		line = strings.TrimSpace(line)

		if line == "" {
			continue
		}

		squote, dquote = false, false
		pair := strings.FieldsFunc(line, func(r rune) bool {
			if r == '\'' && !dquote {
				squote = !squote
			} else if r == '"' && !squote {
				dquote = !dquote
			}

			return !squote && !dquote && unicode.IsSpace(r)
		})

		if len(pair) != 2 {
			return nil, fmt.Errorf("expected pair but found: %s", s.Text())
		}

		for i := 0; i < len(pair); i++ {
			squote, dquote = false, false
			buf := make([]rune, 0, len(pair[i]))
			for _, r := range pair[i] {
				if r == '\'' && !dquote {
					squote = !squote
					continue
				}

				if r == '"' && !squote {
					dquote = !dquote
					continue
				}

				buf = append(buf, r)
			}
			pair[i] = string(buf)
		}

		pairs = append(pairs, pair)
	}

	return pairs, nil
}

// DONE + code review ok
